package apps.sim.action.handler;

import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import apps.sim.core.IHandler;
import apps.sim.core.IHandler.IHandlerConfig;

public final class HandlerFactory {
	private final static HandlerFactory INSTANCE = new HandlerFactory();
	private final Map<Class<?>, IHandler> handlers = new ConcurrentHashMap<Class<?>, IHandler>();
	public final Set<String> PACKAGES = new HashSet<String>(Arrays.asList("apps"));
	public final List<String> CLASSE_NAMES = new ArrayList<String>();
	public final Map<String, Class<?>> CLASSES = new HashMap<String, Class<?>>();
	public URLClassLoader extraLoader = null;

	public static HandlerFactory getInstance() {
		return INSTANCE;
	}

	private HandlerFactory() {
	}

	public void init() {
		final List<URL> urls = new ArrayList<URL>();
		try {

			final File file = new File("extn");
			if (file.exists() && file.isDirectory()) {
				final File[] jars = file.listFiles(new FileFilter() {
					public boolean accept(File pathname) {
						return pathname.getName().endsWith(".jar");
					}
				});
				for (File jar : jars) {
					final URL url = new URL("file:///" + jar.getAbsolutePath().replaceAll("\\\\", "/"));
					urls.add(url);
					final JarFile jf = new JarFile(jar);
					final Enumeration<JarEntry> jarEntries = jf.entries();
					while (jarEntries.hasMoreElements()) {
						final JarEntry nextElement = jarEntries.nextElement();
						if (!nextElement.isDirectory()) {
							String entryName = nextElement.getName();
							if (entryName.endsWith(".class")) {
								final String replaceAll = entryName.replaceAll("/", ".");
								final String clazz = replaceAll.substring(0, replaceAll.length() - 6);
								HandlerFactory.getInstance().CLASSE_NAMES.add(clazz);
							}
						}
					}
					jf.close();
				}
			}
			HandlerFactory.getInstance().extraLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]), Thread.currentThread().getContextClassLoader());
			HandlerFactory.getInstance().resolve();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void resolve() {
		if (extraLoader != null) {
			for (String string : CLASSE_NAMES) {
				try {
					CLASSES.put(string, extraLoader.loadClass(string));
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
	}

	@SuppressWarnings("unchecked")
	public IHandler getRootHandler(String clazz) throws Exception {
		final Class<IHandler> clazzInstance = (Class<IHandler>) getClass(clazz);
		return getHandler(clazzInstance);
	}

	@SuppressWarnings("unchecked")
	public IHandler getHandler(String clazz) throws Exception {
		final Class<IHandler> clazzInstance = (Class<IHandler>) getClass(clazz);
		final IHandler handler = getHandler(clazzInstance);
		if (handler != null) {
			final IHandler newInstance = clazzInstance.newInstance();
			final IHandlerConfig handlerConfiguration = handler.getHandlerConfiguration();
			if (handlerConfiguration != null) {
				newInstance.setHandlerConfiguration(handlerConfiguration.cloneInstance());
			}
			return newInstance;
		}
		return handler;
	}

	public IHandler getHandler(Class<?> clazz) throws Exception {
		if (!clazz.isInterface()) {
			IHandler iHandler = handlers.get(clazz);
			if (iHandler == null) {
				iHandler = (IHandler) clazz.newInstance();
				handlers.put(clazz, iHandler);
			}
			return iHandler;
		}
		return null;
	}

	public Class<?> getClass(String string) throws ClassNotFoundException {
		if (CLASSES.keySet().contains(string)) {
			return CLASSES.get(string);
		}
		return Class.forName(string);
	}

}
